/**
 * 虽然 C 风格字符串文字很好用，但 C 风格字符串变量的行为很奇怪，很难使用（例如，您不能使用赋值来为 C 风格字符串变量分配新值），
 * 并且很危险（例如如果将较大的 C 样式字符串复制到为较短的 C 样式字符串分配的空间中，则会导致未定义的行为）。
 * 在现代 C++ 中，最好避免使用 C 风格的字符串变量。
 * 
 * C++ 在该语言中引入了两种额外的字符串类型，使用起来更加容易和安全：std::string和std::string_view(C++17)。
 * 尽管std::string和std::string_view不是基本类型，但它们非常简单且有用
 * 
 * 
 * 介绍std::string
 *   在 C++ 中使用字符串和字符串对象的最简单方法是通过std::string位于 <string> 头文件中的类型。
 * 
 *   C++ 不会自动将字符串转换为整数或浮点值，反之亦然
 * 
 * std::string可以处理不同长度的字符串
 *   name是用字符串 初始化的"Alex"，该字符串包含五个字符（四个显式字符和一个空终止符）。然后我们设置name一个较大的字符串，然后设置一个较小的字符串。
 *   std::string处理这个没有问题！
 * 
 * 字符串输入std::cin
 *   当使用operator>>从 中提取字符串时std::cin，operator>>仅返回遇到的第一个空格之前的字符。任何其他字符都留在里面std::cin，等待下一次提取。
 * 
 * 使用std::getline()输入文本到变量
 *   要将整行输入读取到字符串中，最好使用该std::getline()函数。std::getline()需要两个参数：第一个是std::cin，第二个是您的字符串变量。
 * 
 * 什么是std::ws
 *   C++也支持输入操纵器，这会改变接受输入的方式。输入std::ws操纵器告诉std::cin在提取之前忽略任何前导空格。前导空白是出现在字符串开头的任何空白字符（空格、制表符、换行符）。
 * 
 *   使用提取运算符 ( >>) 会std::cin忽略前导空格。当遇到非前导空格时，它会停止提取。
 *   std::getline()除非您使用输入操纵器，否则不会忽略前导空格std::ws。当遇到换行符时它会停止提取。
 * 
 * 最佳实践
 *   如果用于std::getline()读取字符串，请使用std::cin >> std::ws输入操纵器忽略前导空格。这需要为每个调用完成std::getline()，因为std::ws不会在调用之间保留。
 * 
 * std::string字符串的长度
 *   length() 是 std::string 的成员函数
 *   尽管std::string要求以 null 终止符（从 C++11 开始），但 a 的返回长度std::string不包括隐式 null 终止符。
 *   注意，std::string::length()返回一个无符号整数值（最有可能为 类型size_t）。如果要将长度分配给int变量，则应该static_cast避免编译器关于有符号/无符号转换的警告
 * 
 * 对于普通函数，我们调用function(object). 对于成员函数，我们调用object.function().
 * 
 * 
 * 初始化字符串的成本很高
 *   每当初始化 std::string 时，都会创建用于初始化它的字符串的副本。复制字符串的成本很高，因此应注意尽量减少复制的数量。
 * 
 * 不要std::string按值传递
 *   std::string按值传递给函数时，std::string必须使用实参实例化并初始化函数形参。这会产生昂贵的副本。
 * 
 * 
 * 双引号字符串文字（例如“Hello, world!”）默认是 C 样式字符串（因此具有奇怪的类型）。
 * 我们可以std::string通过s在双引号字符串文字后面使用后缀来创建具有类型的字符串文字。
 * 
 */


#include <iostream>
#include <string>

int main()
{
    std::cout << "Hello, world!" << "\n"; // "Hello world!" is a C-style string literal.


    std::string empty{ };
    std::cout << '[' << empty << ']' << "\n";

    std::string name { "Alex" }; // initialize name with string literal "Alex"
    name = "John";               // change name to "John"
    std::cout << "My name is: " << name << '\n';


    // std::string yourName {};
    // std::cout << "Enter your name: \n";
    // std::cin >> yourName;
    // std::string yourColor {};
    // std::cout << "Enter your favorite color: \n";
    // std::cin >> yourColor;
    // std::cout << "your name is: " << yourName << "\n";
    // std::cout << "your favorite color is: " << yourColor << "\n";


    // std::cout << "Enter your name: \n";
    // std::getline(std::cin >> std::ws, yourName);
    // std::cout << "Enter your favorite color: \n";
    // std::getline(std::cin >> std::ws, yourColor);
    // std::cout << "your name is: " << yourName << "\n";
    // std::cout << "your favorite color is: " << yourColor << "\n";


    std::cout << "Pick 1 or 2: ";
    int choice{};
    std::cin >> choice;

    std::cout << "Now enter your name: ";
    std::getline(std::cin, name); // note: no std::ws here

    std::cout << "Hello, " << name << ", you picked " << choice << '\n';


    name = "hello world";
    std::cout << name << " has " << name.length() << " characters\n";

    // 在 C++20 中，您还可以使用该std::ssize()函数来获取 a 的长度std::string作为有符号整数值
    // std::cout << name << " has " << std::ssize(name) << " characters\n";


    using namespace std::string_literals; // easy access to the s suffix

    std::cout << "foo\n";   // no suffix is a C-style string literal
    std::cout << "goo\n"s;  // s suffix is a std::string literal


    // quiz
    std::cout << "Enter your full name: \n";
    std::string yourFullName {};
    std::getline(std::cin >> std::ws, yourFullName);
    std::cout << "Enter your age: \n";
    int yourAge {};
    std::cin >> yourAge;
    std::cout << "Your age + length of name is: " << yourAge + static_cast<int>(yourFullName.length()) << "\n";

    return 0;
}